package com.concurrent.sample.business.manager.impl;

import com.concurrent.sample.business.consts.BusinessNatsConsts;
import com.concurrent.sample.business.consts.enums.EventTypeEnum;
import com.concurrent.sample.business.consts.enums.SideEnum;
import com.concurrent.sample.business.dto.TimeRangeDTO;
import com.concurrent.sample.business.dto.trade.CreateTradeEventParamParam;
import com.concurrent.sample.business.manager.ITradeEventService;
import com.concurrent.sample.common.nats.NatsProperties;
import com.concurrent.sample.common.util.JsonUtils;
import com.concurrent.sample.business.dto.debug.AppDebugInfoDTO;
import com.concurrent.sample.business.dto.trade.BaseTradeEventParam;
import com.concurrent.sample.business.dto.trade.TradeExecReportParamParam;
import com.concurrent.sample.business.dto.report.TradeExecReportDTO;
import com.concurrent.sample.business.utils.OrderReportUtils;
import com.concurrent.sample.common.nats.NatsConnFactory;
import io.nats.client.Connection;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class AbstractTradeEventService implements ITradeEventService {

  private ThreadLocal<TimeRangeDTO> timeRangeThreadLocal = new ThreadLocal<>();

  @Resource
  private NatsProperties natsProperties;

  public abstract EventTypeEnum getEventType();

  public abstract SideEnum getSide();

  @Override
  public boolean support(BaseTradeEventParam baseTradeEvent) {
    return getEventType() == baseTradeEvent.getEventType() && getSide() == baseTradeEvent.getSide();
  }

  @Override
  public void handleTrade(CreateTradeEventParamParam createTradeEventParam) {
    beforeHandleTrade(createTradeEventParam);
    handlingTrade(createTradeEventParam);
    afterHandleTade(createTradeEventParam);
  }

  public void beforeHandleTrade(CreateTradeEventParamParam createTradeEventParam) {
    TimeRangeDTO timeRangeDTO = new TimeRangeDTO();
    timeRangeDTO.setStartTime(System.currentTimeMillis());
    timeRangeThreadLocal.set(timeRangeDTO);
  }

  public void afterHandleTade(CreateTradeEventParamParam createTradeEventParam) {
    TimeRangeDTO timeRangeDTO = timeRangeThreadLocal.get();
    log.info("cost time = {} ms", System.currentTimeMillis() - timeRangeDTO.getStartTime());
    timeRangeThreadLocal.remove();
  }

  public void handlingTrade(CreateTradeEventParamParam tradeEventParam) {
    // 场内下单，模拟撮合成交
    List<TradeExecReportDTO> execReportDTOS = OrderReportUtils.buildExecReport(tradeEventParam.getOrder());

    Connection nc = NatsConnFactory.getSingle(natsProperties);

    for(TradeExecReportDTO execReport : execReportDTOS){
      nc.publish(BusinessNatsConsts.TRADE_EXEC_REPORT_TOPIC, JsonUtils.toJson(execReport).getBytes(StandardCharsets.UTF_8));
    }
  }

  @Override
  public void handleTradeExecReport(TradeExecReportParamParam tradeExecReportParam) {
    beforeHandleTradeExecReport(tradeExecReportParam);
    handlingTradeExecReport(tradeExecReportParam);
    afterHandleTradeExecReport(tradeExecReportParam);
  }

  public void beforeHandleTradeExecReport(TradeExecReportParamParam tradeExecReportParam) {
    TimeRangeDTO timeRangeDTO = new TimeRangeDTO();
    timeRangeDTO.setStartTime(System.currentTimeMillis());
    timeRangeThreadLocal.set(timeRangeDTO);
  }

  public abstract void handlingTradeExecReport(TradeExecReportParamParam tradeExecReportParam);

  public void afterHandleTradeExecReport(TradeExecReportParamParam tradeExecReportParam) {
    TimeRangeDTO timeRangeDTO = timeRangeThreadLocal.get();
    log.info("cost time = {} ms", System.currentTimeMillis() - timeRangeDTO.getStartTime());
    timeRangeThreadLocal.remove();

    AppDebugInfoDTO.endTime = System.currentTimeMillis();
  }

}
